﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using System.IO;

namespace LNUGame.Model
{
    /// <summary>
    /// Represents a playable level
    /// </summary>
    class Level
    {
        public enum Tile
        {
            EMPTY = 0,
            GROUND_TOP = 1,
            UNDERGROUND = 2,
            SPIKES = 3,
            ENEMY_SPAWN = 4,
            HEALTH_PACK = 5,
            END_OF_LEVEL = 6,
            EMPTY_BLOCKED
        }

        public enum Levels
        {
            FIRST = 0,
            SECOND = 1,
            LAST = 2
        }

        List<Enemy> m_enemyList = new List<Enemy>(0);
        List<Healthpack> m_healthpackList = new List<Healthpack>(0);

        private Levels m_currentLevel;

        internal Tile[,] m_tiles;

        internal const int LEVEL_WIDTH = 200;
        internal const int LEVEL_HEIGHT = 13;
        
        private const string LEVEL1 = "level1.txt";
        private const string LEVEL2 = "level2.txt";
        private const string LEVEL3 = "level3.txt";

        /// <summary>
        /// Main constructor.
        /// Sets the level width and height.
        /// Loads the first level.
        /// </summary>
        public Level()
        {
            m_tiles = new Tile[LEVEL_WIDTH, LEVEL_HEIGHT];
            m_currentLevel = Levels.FIRST;
            LoadLevel();
        }

        /// <summary>
        /// Loads the currently selected level.
        /// </summary>
        internal void LoadLevel()
        {
            switch (m_currentLevel)
            {
                case Levels.FIRST:
                    GenerateLevel(LEVEL1);
                    break;
                case Levels.SECOND:
                    GenerateLevel(LEVEL2);
                    break;
                case Levels.LAST:
                    GenerateLevel(LEVEL3);
                    break;
            }
        }

        /// <summary>
        /// Generates a level from a textfile
        /// </summary>
        /// <param name="a_level">Filename of the level we should genereate</param>
        private void GenerateLevel(string a_level)
        {
            m_enemyList.Clear();
            m_healthpackList.Clear();

            StreamReader levelReader = new StreamReader(a_level);

            String line = String.Empty;

            for (int y = 0; y < LEVEL_HEIGHT; y++ )
            {
                line = levelReader.ReadLine();

                for (int x = 0; x < LEVEL_WIDTH; x++)
                {
                    if (line.ElementAt(x).Equals(' '))
                    {
                        m_tiles[x, y] = Tile.EMPTY;
                    }
                    else if (line.ElementAt(x).Equals('#'))
                    {
                        m_tiles[x, y] = Tile.GROUND_TOP;
                    }
                    else if (line.ElementAt(x).Equals('U'))
                    {
                        m_tiles[x, y] = Tile.UNDERGROUND;
                    }
                    else if (line.ElementAt(x).Equals('S'))
                    {
                        m_tiles[x, y] = Tile.SPIKES;
                    }
                    else if (line.ElementAt(x).Equals('E'))
                    {
                        m_tiles[x, y] = Tile.ENEMY_SPAWN;
                        m_enemyList.Add(new Enemy(new Vector2(x, y+1)));
                    }
                    else if (line.ElementAt(x).Equals('P'))
                    {
                        m_tiles[x, y] = Tile.HEALTH_PACK;
                        m_healthpackList.Add(new Healthpack(new Vector2(x, y+1)));
                    }
                    else if (line.ElementAt(x).Equals('D'))
                    {
                        m_tiles[x, y] = Tile.END_OF_LEVEL;
                    }
                    else if (line.ElementAt(x).Equals('B'))
                    {
                        m_tiles[x, y] = Tile.EMPTY_BLOCKED;
                    }
                }
            }
        }

        /// <summary>
        /// Checks if a sprite is standing in a spike trap.
        /// </summary>
        /// <param name="a_position">The position of the sprite</param>
        /// <param name="a_size">The size of the sprite</param>
        /// <returns>True of the sprite is standing in a sprike trap, false otherwise</returns>
        internal bool IsStandingInSpikes(Vector2 a_position, Vector2 a_size)
        {
            Vector2 topLeft = new Vector2(a_position.X - a_size.X / 2.0f, a_position.Y - a_size.Y);
            Vector2 bottomRight = new Vector2(a_position.X + a_size.X / 2.0f, a_position.Y);


            for (int x = 0; x < LEVEL_WIDTH; x++)
            {
                for (int y = 0; y < LEVEL_HEIGHT; y++)
                {

                    if (bottomRight.X < (float)x)
                        continue;
                    if (bottomRight.Y < (float)y)
                        continue;
                    if (topLeft.X > (float)x + 1.0f)
                        continue;
                    if (topLeft.Y > (float)y + 1.0f)
                        continue;

                    if (m_tiles[x, y] == Tile.SPIKES)
                    {
                        return true;
                    }
                }
            }
            return false;
        }

        /// <summary>
        /// Checks if the player is finishing a level (i.e colliding with an END_OF_LEVEL tile)
        /// </summary>
        /// <param name="a_position">The position of the player</param>
        /// <param name="a_size">The size of the player</param>
        /// <returns>True if the player is finishing the level, false otherwise</returns>
        internal bool IsFinishingLevel(Vector2 a_position, Vector2 a_size)
        {
            Vector2 topLeft = new Vector2(a_position.X - a_size.X / 2.0f, a_position.Y - a_size.Y);
            Vector2 bottomRight = new Vector2(a_position.X + a_size.X / 2.0f, a_position.Y);

            for (int x = 0; x < LEVEL_WIDTH; x++)
            {
                for (int y = 0; y < LEVEL_HEIGHT; y++)
                {

                    if (bottomRight.X < (float)x)
                        continue;
                    if (bottomRight.Y < (float)y)
                        continue;
                    if (topLeft.X > (float)x + 1.0f)
                        continue;
                    if (topLeft.Y > (float)y + 1.0f)
                        continue;

                    if (m_tiles[x, y] == Tile.END_OF_LEVEL)
                    {
                        return true;
                    }
                }
            }
            return false;
        }

        /// <summary>
        /// Checks if the player is colliding with the world (i.e a GROUND_TOP or UNDERGROUND tile)
        /// </summary>
        /// <param name="a_position">The position of the player</param>
        /// <param name="a_size">The size of the player</param>
        /// <returns>True if the player is colliding with the world</returns>
        internal bool IsCollidingAt(Vector2 a_position, Vector2 a_size)
        {
            Vector2 topLeft = new Vector2(a_position.X - a_size.X / 2.0f, a_position.Y - a_size.Y);
            Vector2 bottomRight = new Vector2(a_position.X + a_size.X / 2.0f, a_position.Y);


            for (int x = 0; x < LEVEL_WIDTH; x++)
            {
                for (int y = 0; y < LEVEL_HEIGHT; y++)
                {

                    if (bottomRight.X < (float)x)
                        continue;
                    if (bottomRight.Y < (float)y)
                        continue;
                    if (topLeft.X > (float)x + 1.0f)
                        continue;
                    if (topLeft.Y > (float)y + 1.0f)
                        continue;

                    if (m_tiles[x, y] == Tile.GROUND_TOP || m_tiles[x, y] == Tile.UNDERGROUND || m_tiles[x, y] == Tile.EMPTY_BLOCKED)
                    {
                        return true;
                    }
                }
            }
            return false;
        }

        /// <summary>
        /// Checks if a sprite is standing over a gap, spikes or underground terrain
        /// </summary>
        /// <param name="a_position">The position of the sprite</param>
        /// <returns>True if the sprite is standing over a gap, spikes or underground terrain, false otherwise</returns>
        internal bool IsEnemyStandingOverGap(Vector2 a_position)
        {
            if ((m_tiles[(int)a_position.X, (int)a_position.Y] == Tile.EMPTY) ||
                (m_tiles[(int)a_position.X, (int)a_position.Y] == Tile.SPIKES) ||
                (m_tiles[(int)a_position.X, (int)a_position.Y] == Tile.UNDERGROUND))
            {
                return true;
            }
            return false;
        }

        /// <summary>
        /// Getter and setter for the current level
        /// </summary>
        public Levels CurrentLevel
        {
            get
            {
                return m_currentLevel;
            }
            set
            {
                m_currentLevel = value;
            }
        }

        /// <summary>
        /// Getter for the enemies on the level
        /// </summary>
        public List<Enemy> Enemies
        {
            get
            {
                return m_enemyList;
            }
        }

        /// <summary>
        /// Getter for the healthpacks on the level
        /// </summary>
        public List<Healthpack> Healthpacks
        {
            get
            {
                return m_healthpackList;
            }
        }
    }
}
